package com.sts.security;

import com.sts.dto.TicketCommentDTO;
import com.sts.dto.TicketDTO;
import com.sts.service.TicketCommentService;
import com.sts.service.TicketService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

@Slf4j
@Component("ticketSecurity")
@RequiredArgsConstructor
public class TicketSecurity {

    private final TicketCommentService commentService;
    private final TicketService ticketService;

    /* =========================================================
       1. 供 @PreAuthorize 调用的入口
       ========================================================= */
    public boolean canAccess(Long ticketId, Authentication auth) {
        Long authorId = getTicketAuthorId(ticketId);
        Long userId   = getUserId(auth);
        boolean isAuthor = authorId != null && authorId.equals(userId);
        boolean isAdmin  = hasRole(auth, "ADMIN");

        log.debug("ticketId={}, authorId={}, currentUserId={}, isAuthor={}, isAdmin={}",
                ticketId, authorId, userId, isAuthor, isAdmin);
        return isAuthor || isAdmin;
    }

    /* =========================================================
       2. 原有方法——判断评论作者
       ========================================================= */
    public boolean isCommentAuthor(Long commentId, Authentication auth) {
        TicketCommentDTO comment = commentService.detail(commentId);
        return comment != null && getUserId(auth).equals(comment.getAuthorId());
    }

    /* =========================================================
       3. 私有工具
       ========================================================= */
    private boolean hasRole(Authentication auth, String role) {
        return auth.getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .anyMatch(r -> ("ROLE_" + role).equals(r));
    }

    /**
     * 根据 ticketId 查作者 ID —— 请按自己项目实际替换
     */
    /**
     * 根据 ticketId 查询工单作者的用户ID
     */
    private Long getTicketAuthorId(Long ticketId) {
        // 1. 查工单
        TicketDTO ticket = ticketService.detail(ticketId); // 或 getById(ticketId)
        if (ticket == null) {
            // 工单不存在，认为“无作者”，返回 null 后 canAccess 会返回 false
            return null;
        }
        // 2. 取作者ID
        return ticket.getCreatorId();
    }

    /**
     * 核心：把 Authentication 里的 principal 转成用户 ID
     * 支持三种常见场景，按需要保留/删除
     */
    private Long getUserId(Authentication auth) {
        if (auth == null || !auth.isAuthenticated()) {
            throw new IllegalStateException("用户未登录");
        }

        Object principal = auth.getPrincipal();

        /* ① 如果生成 Authentication 时直接把 userId 放进了 principal */
        if (principal instanceof Long) {
            return (Long) principal;
        }
        if (principal instanceof Integer) {
            return ((Integer) principal).longValue();
        }

        /* ② 如果 principal 是你自定义的 LoginUser 对象 */
        if (principal instanceof LoginUser) {          // 类名按你的实际改
            return ((LoginUser) principal).getUserId();
        }

        /* ③ 如果 principal 只是用户名（字符串），需要反查 ID */
        if (principal instanceof String) {
            String userName = (String) principal;
            // 3-A 直接解析数字（旧数据兼容）
            try {
                return Long.valueOf(userName);
            } catch (NumberFormatException ignored) {
            }
            // 3-B 按用户名查库（推荐）
            // return userService.getIdByUserName(userName);
            // 临时兜底：无法判断时返回 0L，表示“不是作者”
            return 0L;
        }

        throw new IllegalArgumentException("不支持的 principal 类型：" + principal.getClass());
    }

    /* =========================================================
       4. 如果你用了自定义 LoginUser，可以建一个简单静态类
          不用就删掉
       ========================================================= */
    public static class LoginUser {
        private final Long userId;
        public LoginUser(Long userId) { this.userId = userId; }
        public Long getUserId() { return userId; }
    }
}